/*
 * ManageCacheFile.c
 *
 *  Created on: 30/10/2011
 *      Author: utn_so
 */
#include "ManageCacheFile.h"

//Variable global externa
extern struct bootSector* bootSectorDisco;


//Mutex para administrar el recurso del vector de la cache
extern pthread_mutex_t mutex_cacheVector;

//Mutex para administrar la entrada de archivos de la cache
extern pthread_mutex_t mutex_cacheFile;

//interface de inicializacion
void initializeCacheFile(){
	cacheList = (cacheFile *) malloc(sizeof(cacheFile));
}

//interface para crear una entrada Para un archivo en la cache
extern int32_t addCacheFile(const char* nameOfFile, ListaDirectorios* directoryList){

	pthread_mutex_lock(&mutex_cacheFile);

	/*	verificar que ya no exita entrada, multi thread	*/
	ListaDirectorios* dir = buscarArchivoCache(nameOfFile);

	if (dir != NULL)
			return -1;

	cacheFile *aux;
	cacheFile *nuevo = malloc(sizeof(cacheFile));

	nuevo-> dataVector = malloc(sizeof(cacheData)*(sizeOfCacheData));
	inicializarCache(nuevo -> dataVector);
/* Innecesario
	//	reservo memoria para cada bloque
	int32_t i;
	for (i=0;i<sizeOfCacheData;i++)
		nuevo->dataVector[i].bloque =malloc(sizeof(char)*4096);
*/
	nuevo->listaDirectorios = directoryList;

	nuevo -> nameOfFile = malloc(sizeof(char)*20);
	strcpy(nuevo->nameOfFile, nameOfFile);

	nuevo->numberOfInstance = 1;
	nuevo->sig = NULL;

	if(cacheList == NULL){
		cacheList = nuevo;
	}
	else{
		aux = cacheList;
		while(aux->sig != NULL){
			aux = aux->sig;
		}
		aux->sig = nuevo;
	}
	pthread_mutex_unlock(&mutex_cacheFile);
	return 0;

}

//interface Para Busqueda de clusters
extern int32_t searchCacheData(const char* pathSearch, uint32_t idBloque, unsigned char* buffer)
{
	pthread_mutex_lock(&mutex_cacheVector);

	cacheFile* file = searchCacheFile(pathSearch);
	if(file != NULL){
		uint32_t i = 0;
		cacheData* bloques = (file->dataVector);

		for(i = 0; i < sizeOfCacheData; i++)
		{
			//devuelve una copia para evitar inconsistencias en la multi programacion
			cacheData* cacheFound = buscarBloque(bloques, idBloque);
			if(cacheFound != NULL){
				memcpy(buffer,cacheFound->bloque,4096);
				pthread_mutex_unlock(&mutex_cacheVector);
				return 0;
			}
		}
		pthread_mutex_unlock(&mutex_cacheVector);
		return -1;
	}
	pthread_mutex_unlock(&mutex_cacheVector);
	return -1;
}

uint32_t inicializarCache(cacheData *vectorCache){
	uint32_t i = 0;
		for (i=0;i<sizeOfCacheData;i++)
			{
			vectorCache[i].bloque = NULL;
			vectorCache[i].markOfWrite = -1;
			vectorCache[i].idBloque = -1;
			}

		return 0;
}

//borrar una archivo de la cache con flush
extern int32_t deleteCacheFileFlush(const char* file){

	/*	verificar que ya no exita entrada, multi thread	*/
	ListaDirectorios* dir = buscarArchivoCache(file);

	if(dir==NULL)
		return -1;

	cacheFile* aux = cacheList;
	cacheFile* ant = NULL;

	while((aux->sig != NULL) && (strcmp(aux->nameOfFile, file) != 0)){
		ant = aux;
		aux = aux->sig;
	}

	if(strcmp(aux->nameOfFile, file) == 0){
		flushCacheFile(aux->dataVector);
		if(ant != NULL){
			ant->sig = aux->sig;
		}else{
			cacheList = aux->sig;
		}

		int32_t i;
		for (i=0;i<sizeOfCacheData;i++)
			if(aux->dataVector[i].idBloque != -1){
				//Esto modifique 26/12/2012
				aux->dataVector[i].idBloque = -1;
				free(aux->dataVector[i].bloque);
			}

		free(aux -> dataVector);
		free(aux -> listaDirectorios -> directorio);
		free(aux -> listaDirectorios -> nombreLargo);
		free(aux -> listaDirectorios);
		free(aux -> nameOfFile);
		free(aux);
		return 0;
	}
	return -1;
}

//En realidad no elimina, quedo asi por una vieja definicon que luego se cambio
extern ListaDirectorios* deleteCacheFileFlushTruncate(const char* file){

	cacheFile* cf;
	cf = searchCacheFile(file);

	if(cf==NULL)
		return NULL;

	flushCacheFile(cf->dataVector);
	return cf->listaDirectorios;
}

//borra un archivo de la cache sin flush
int32_t deleteCacheFileNoFlush(const char* file){

	/*	verificar que ya no exita entrada, multi thread	*/
	ListaDirectorios* dir = buscarArchivoCache(file);

	if(dir==NULL)
		return -1;

	cacheFile* aux = cacheList;
		cacheFile* ant = NULL;

		while((aux->sig != NULL) && (strcmp(aux->nameOfFile, file) != 0)){
			ant = aux;
			aux = aux->sig;
		}

		if(strcmp(aux->nameOfFile, file) == 0){
			if(ant != NULL){
				ant->sig = aux->sig;
			}else{
				cacheList = aux->sig;
			}

			int32_t i;
			for (i=0;i<sizeOfCacheData;i++)
				if(aux->dataVector[i].idBloque != -1){
					//Modifique 26/12/2011
					aux->dataVector[i].idBloque = -1;
					free(aux->dataVector[i].bloque);
				}

			free(aux -> dataVector);
			free(aux -> listaDirectorios -> directorio);
			free(aux -> listaDirectorios -> nombreLargo);
			free(aux -> listaDirectorios);
			free(aux -> nameOfFile);
			free(aux);
			return 0;
		}
		return -1;
}

cacheFile* searchCacheFile(const char* file){
	cacheFile *aux = cacheList;

	while((aux != NULL) && (strcmp(aux->nameOfFile, file) != 0)){
		aux = aux->sig;
	}
	return aux;
}

void correrUnaPosicionFrom(cacheData *vectorCache, uint32_t pos){
		uint32_t i;
		uint32_t tope = sizeOfCacheData - 1;
		for(i = pos; i < tope; i++){
			vectorCache[i] = vectorCache[i+1];
		}
}

cacheData* buscarBloque(cacheData *vectorCache,uint32_t idBloque){
			uint32_t i;
			for(i = 0; i < sizeOfCacheData; i++){
				if(vectorCache[i].idBloque ==  idBloque)
					{
//					pthread_mutex_lock(&mutex_cacheVector);
					cacheData temp = vectorCache[i];
					correrUnaPosicionFrom(vectorCache, i);
					vectorCache[sizeOfCacheData - 1] = temp;
//					pthread_mutex_unlock(&mutex_cacheVector);
					return &vectorCache[sizeOfCacheData - 1];
					}
			}
			return NULL;
}

ListaDirectorios* buscarArchivoCache(const char * path){

	cacheFile* file;
	file = searchCacheFile(path);

	if(file != NULL)
		return file->listaDirectorios;

	return NULL;

}


//interface para AgregarCache
int32_t addCacheData(const char* path, uint32_t idCluster, unsigned char* cluster, uint32_t operacion, uint32_t sectorInicio){

	cacheData newCacheData;
	newCacheData.bloque = malloc(sizeof(char)*4096);
	memcpy(newCacheData.bloque,cluster,4096);

	newCacheData.idBloque = idCluster;
	newCacheData.markOfWrite = operacion;
	newCacheData.sectorIncicio = sectorInicio;

	cacheFile* file = searchCacheFile(path);
	if(file != NULL){
		cacheData* bloques = (file->dataVector);
		int i;
		//verifico si ya existe,si existe actualizo el contenido.
		for(i = 0; i < sizeOfCacheData; i++){
			if(bloques[i].idBloque == idCluster){

				// si la operacion es de solo lectura, el contenido es el mismo.
				//No actualio nada.
				if(operacion == 0){
					return 1;
				//si la operacion es de escritura entonces si actualizo
				}else{
					free(bloques[i].bloque);
					bloques[i].bloque = newCacheData.bloque;
					bloques[i].markOfWrite = operacion;
					return i;
				}
			}
		}
		// si no lo encontro lo agrego.
		return agregarCache(bloques, newCacheData);;
	}
	return -1;
}

int32_t agregarCache(cacheData *vectorCache, cacheData newCache){

		pthread_mutex_lock(&mutex_cacheVector);
		int32_t res;

		if(vectorCache[sizeOfCacheData - 1].idBloque == -1)
			{
			res = insercionEnVectorInicial(vectorCache, newCache);
			pthread_mutex_unlock(&mutex_cacheVector);
			return res;
			}

		else
			{
			res = insercionPorCola( vectorCache, newCache);
			pthread_mutex_unlock(&mutex_cacheVector);
			return res;
			}
}

int32_t insercionEnVectorInicial(cacheData *vectorCache, cacheData newCache){
	uint32_t i;
		for(i = 0; i < sizeOfCacheData; i++){
			if(vectorCache[i].idBloque == -1){
				vectorCache[i] = newCache;
				return i;
			}
		}
		return -1;
}

int32_t insercionPorCola(cacheData *vectorCache, cacheData newCache){

	uint32_t i;
			uint32_t tope = sizeOfCacheData - 1;
			for(i = 0; i < tope; i++){
				//al insertar por cola va a desaparecer la posicio 0
				//verifico si tiene marca de escritura
				if(vectorCache[0].markOfWrite == 1){
					persistirCluster(bootSectorDisco, vectorCache[i].idBloque , vectorCache[i].bloque );
				}
				if(i == 0){
					free(vectorCache[0].bloque);
				}
				vectorCache[i] = vectorCache[i+1];
			}
			vectorCache[sizeOfCacheData - 1] = newCache;
			return sizeOfCacheData;
}

void establecerTamanioDeLaCache(uint32_t sizeCache){
	sizeOfCacheData = (uint32_t)(sizeCache / 4);
}

void flushCacheDataForFile(const char* path){
	cacheFile* file = searchCacheFile(path);
	if(file != NULL){
		flushCacheFile(file->dataVector);
	}
}

void flushCacheFile(cacheData *vectorCache){
	uint32_t i;
	for(i = 0; i < sizeOfCacheData; i++){
		if(vectorCache[i].markOfWrite == 1){
			//	persistencia de cluster
			persistirCluster(bootSectorDisco, vectorCache[i].idBloque , vectorCache[i].bloque );
			// se pasa a lectura
			vectorCache[i].markOfWrite = 0;
		}
	}
}

uint32_t renameCacheFile(const char* pathViejo,const char* pathNvo, ListaDirectorios* newList){

	cacheFile* file = searchCacheFile(pathViejo);

		if(file != NULL){
//EN LA BUSQUEDA SE DEVUELVE EL ORIGINAL POR ESO NO ES NECESARIO COPIAR NADA
			strcpy(file->nameOfFile, pathNvo);

			return 0;
		}else{
			return -1;
		}
}

void treatSignalForCache(int32_t signal){
	escribir_logfile(NOMPS, 0, INFO, "señal archivo cache recibida");
	escribirLogCacheFile();
}
